package org.javacoo.cowswing.ui.widget;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.plaf.basic.BasicTabbedPaneUI;
import javax.swing.plaf.metal.MetalTabbedPaneUI;

  
/**
 * @version 1.0 07/09/99
 */
public class SingleRowTabbedPaneUI extends MetalTabbedPaneUI {

  protected ActionListener[]  buttonListeners;
  
  public void installUI(JComponent c) {
    this.tabPane = (JTabbedPane)c;
    c.setLayout(createLayoutManager());
    installDefaults(); 
    installComponents();
    installListeners();
    installKeyboardActions();
        
    runCount    = 1;
    selectedRun = 0;   
  }
    
  public void uninstallUI(JComponent c) {
    uninstallComponents();
    super.uninstallUI(c);
  }
  
  protected LayoutManager createLayoutManager() {
    return new SingleRowTabbedLayout(tabPane);
  }
    
  protected void installComponents() {
    JButton[] buttons = ((SingleRowTabbedPane)tabPane).getButtons();
    for (int i=0;i<buttons.length;i++) {
      tabPane.add(buttons[i]);
    }
  }

  protected void uninstallComponents() {
    JButton[] buttons = ((SingleRowTabbedPane)tabPane).getButtons();
    for (int i=0;i<buttons.length;i++) {
      tabPane.remove(buttons[i]);
    }
  }
        
  protected void installListeners() {
    super.installListeners();
    SingleRowTabbedPane stabPane = (SingleRowTabbedPane)tabPane;
    JButton[] buttons = stabPane.getButtons();
    int n = buttons.length;
    buttonListeners = new ActionListener[n];
    
    for (int i=0;i<n;i++) {
      buttonListeners[i] = null;
      String str = buttons[i].getActionCommand();
      
      if (str.equals(SingleRowTabbedPane.ROTATE)) {
        buttonListeners[i] = new ShiftTabs() {
          protected int getStartIndex() {
            int index = sPane.getVisibleStartIndex() + sPane.getVisibleCount();            
            return (index < sPane.getTabCount())? index: 0;
          }
        };
      }
      else if (str.equals(SingleRowTabbedPane.PREVIOUS)) {
        buttonListeners[i] = new ShiftTabs() {
          protected int getStartIndex() {
            return getStartIndex(sPane.getVisibleStartIndex() - 1);
          }
        };
      }
      else if (str.equals(SingleRowTabbedPane.NEXT)) {
        buttonListeners[i] = new ShiftTabs() {
          protected int getStartIndex() {
            return sPane.getVisibleStartIndex() + sPane.getVisibleCount();
          }
        };
      }
      else if (str.equals(SingleRowTabbedPane.FIRST)) {
        buttonListeners[i] = new ShiftTabs();
      }
      else if (str.equals(SingleRowTabbedPane.LEFT_SHIFT)) {
        buttonListeners[i] = new ShiftTabs() {
          protected int getStartIndex() {
            return sPane.getVisibleStartIndex() - 1;
          }
        };
      }
      else if (str.equals(SingleRowTabbedPane.RIGHT_SHIFT)) {
        buttonListeners[i] = new ShiftTabs() {
          protected int getStartIndex() {
            return sPane.getVisibleStartIndex() + 1;
          }
        };
      }
      else if (str.equals(SingleRowTabbedPane.LAST)) {
        buttonListeners[i] = new ShiftTabs() {
          protected int getStartIndex() {
            return getStartIndex(sPane.getTabCount() - 1);
          }
        };
      }       
      buttons[i].addActionListener(buttonListeners[i]);
    }
  }
        
  protected void uninstallListeners() {
    super.uninstallListeners();
    JButton[] buttons = ((SingleRowTabbedPane)tabPane).getButtons();
    for (int i=0;i<buttons.length;i++) {
      buttons[i].removeActionListener(buttonListeners[i]);
    }
  }
  
  public int tabForCoordinate(JTabbedPane pane, int x, int y) {
    int tabCount = tabPane.getTabCount();
    SingleRowTabbedPane stabPane = (SingleRowTabbedPane)tabPane;
    int visibleCount      = stabPane.getVisibleCount();
    int visibleStartIndex = stabPane.getVisibleStartIndex();

    for (int i=0,index = visibleStartIndex; i < visibleCount; i++,index++) {
      if (rects[index].contains(x, y)) {
        return index;
      }
    }
    return -1;
  }
  
  public void paint(Graphics g, JComponent c) {
    int selectedIndex = tabPane.getSelectedIndex();
    int tabPlacement = tabPane.getTabPlacement();
    int tabCount = tabPane.getTabCount();
        
    ensureCurrentLayout();

    SingleRowTabbedPane stabPane = (SingleRowTabbedPane)tabPane;
    int visibleCount      = stabPane.getVisibleCount();
    int visibleStartIndex = stabPane.getVisibleStartIndex();
    
    Rectangle iconRect = new Rectangle(),
              textRect = new Rectangle();
    Rectangle clipRect = g.getClipBounds();  
    Insets insets = tabPane.getInsets();
    
    tabRuns[0] = visibleStartIndex;

    for (int i=0,index=visibleStartIndex; i<visibleCount; i++,index++) {          
      if (rects[index].intersects(clipRect)) {
        paintTab(g, tabPlacement, rects, index, iconRect, textRect);
      }
    }
    if (stabPane.isVisibleTab(selectedIndex)) {
      if (rects[selectedIndex].intersects(clipRect)) {
        paintTab(g, tabPlacement, rects, selectedIndex, iconRect, textRect);
      }
    }

    paintContentBorder(g, tabPlacement, selectedIndex);
  }
    

  protected void paintContentBorderTopEdge( Graphics g,
        int tabPlacement, int selectedIndex, int x, int y, int w, int h ) {    
    g.setColor(selectHighlight);
    if (tabPlacement != TOP || selectedIndex < 0 || 
        (rects[selectedIndex].y + rects[selectedIndex].height + 1 < y) ||
        !((SingleRowTabbedPane)tabPane).isVisibleTab(selectedIndex) ) {
      g.drawLine(x, y, x+w-2, y);
    } else {
      Rectangle selRect = rects[selectedIndex];
      g.drawLine(x, y, selRect.x + 1, y);
      if (selRect.x + selRect.width < x + w - 2) {
        g.drawLine(selRect.x + selRect.width, y, x+w-2, y);
      } else {
	g.setColor(shadow); 
        g.drawLine(x+w-2, y, x+w-2, y);
      }
    }
  }

  protected void paintContentBorderBottomEdge(Graphics g,
        int tabPlacement, int selectedIndex, int x, int y, int w, int h) { 
    g.setColor(darkShadow);
    if (tabPlacement != BOTTOM || selectedIndex < 0 ||
        (rects[selectedIndex].y - 1 > h) ||
        !((SingleRowTabbedPane)tabPane).isVisibleTab(selectedIndex) ) {
      g.drawLine(x, y+h-1, x+w-1, y+h-1);
    } else {
      Rectangle selRect = rects[selectedIndex];
      g.drawLine(x, y+h-1, selRect.x, y+h-1);
      if (selRect.x + selRect.width < x + w - 2) {
        g.drawLine(selRect.x + selRect.width, y+h-1, x+w-1, y+h-1);
      } 
    }
  }
    
    

  protected Insets getTabAreaInsets(int tabPlacement) {
    SingleRowTabbedPane stabPane = (SingleRowTabbedPane)tabPane;
    Dimension d = stabPane.getButtonPreferredSize();
    int n = stabPane.getButtonCount();      
    int buttonPlacement = stabPane.getButtonPlacement();
      
    Insets currentInsets = new Insets(0,0,0,0);
    if (tabPlacement == TOP) {
      currentInsets.top    = tabAreaInsets.top;
      currentInsets.bottom = tabAreaInsets.bottom;        
    } else {
      currentInsets.top    = tabAreaInsets.bottom;
      currentInsets.bottom = tabAreaInsets.top;        
    }
    if (buttonPlacement == RIGHT) {
      currentInsets.left   = tabAreaInsets.left;
      currentInsets.right  = tabAreaInsets.right + n * d.width;        
    } else {
      currentInsets.left   = tabAreaInsets.left  + n * d.width;
      currentInsets.right  = tabAreaInsets.right;        
    }
    return currentInsets;
  }

  protected int lastTabInRun(int tabCount, int run) {
    SingleRowTabbedPane stabPane = (SingleRowTabbedPane)tabPane;
    return stabPane.getVisibleStartIndex() + stabPane.getVisibleCount() -1;            
  }

  protected void ensureCurrentLayout() {         
    SingleRowTabbedLayout layout = (SingleRowTabbedLayout)tabPane.getLayout();
    layout.calculateLayoutInfo(); 
    setButtonsEnabled();  
  }  
    
  protected void setButtonsEnabled() {
    SingleRowTabbedPane stabPane = (SingleRowTabbedPane)tabPane;
    int visibleCount      = stabPane.getVisibleCount();
    int visibleStartIndex = stabPane.getVisibleStartIndex();
    JButton[] buttons = stabPane.getButtons();
    boolean lEnable = 0 < visibleStartIndex;
    boolean rEnable = visibleStartIndex + visibleCount < tabPane.getTabCount();
    for (int i=0;i<buttons.length;i++) {
      boolean enable = false;
      String str = buttons[i].getActionCommand();
      if (str.equals(SingleRowTabbedPane.ROTATE)) {
        enable = lEnable || rEnable;
      }
      else if (str.equals(SingleRowTabbedPane.PREVIOUS)) {
        enable = lEnable;
      }
      else if (str.equals(SingleRowTabbedPane.NEXT)) {
        enable = rEnable;
      }
      else if (str.equals(SingleRowTabbedPane.FIRST)) {
        enable = lEnable;
      }
      else if (str.equals(SingleRowTabbedPane.LEFT_SHIFT)) {
        enable = lEnable;
      }
      else if (str.equals(SingleRowTabbedPane.RIGHT_SHIFT)) {
        enable = rEnable;
      }
      else if (str.equals(SingleRowTabbedPane.LAST)) {
        enable = rEnable;
      }
      buttons[i].setEnabled(enable);
    }   
  }     

   
  // 
  // Tab Navigation by Key 
  // (Not yet done)
  //
  protected void ensureVisibleTabAt(int index) { 
    SingleRowTabbedPane stabPane = (SingleRowTabbedPane)tabPane;
    int visibleCount      = stabPane.getVisibleCount();
    int visibleStartIndex = stabPane.getVisibleStartIndex();
    int visibleEndIndex   = visibleStartIndex + visibleCount -1;

    if (visibleStartIndex < index                &&
                            index < visibleEndIndex) {
      return;
    }
    int selectedIndex = tabPane.getSelectedIndex();
    boolean directionIsRight = (0 < index - selectedIndex)? true: false;
    //if (directionIsRight) {
      if (index <= visibleStartIndex) {
        //System.out.println("dec");
        if (visibleStartIndex == 0) return;
        stabPane.setVisibleStartIndex( --visibleStartIndex );
        ((SingleRowTabbedLayout)tabPane.getLayout()).calculateLayoutInfo();
        int count      = stabPane.getVisibleCount();
        int startIndex = stabPane.getVisibleStartIndex();        
        if (startIndex <= index                &&
                          index <= startIndex + count-1) {
        } else {
          stabPane.setVisibleStartIndex( ++visibleStartIndex );
        }
      }
    //} else {
      if (visibleEndIndex <= index) {
        //System.out.println("inc");
        if (visibleStartIndex == visibleCount+1) return;
        stabPane.setVisibleStartIndex( ++visibleStartIndex );
        ((SingleRowTabbedLayout)tabPane.getLayout()).calculateLayoutInfo();
        int count      = stabPane.getVisibleCount();
        int startIndex = stabPane.getVisibleStartIndex();        
        if (startIndex <= index                &&
                          index <= startIndex + count-1) {
        } else {
          stabPane.setVisibleStartIndex( --visibleStartIndex );
        }
      }
    //}
      
    int c      = stabPane.getVisibleCount();
    int s = stabPane.getVisibleStartIndex();    
  }

  protected void selectNextTab(int current) {
    for (int i=current+1;i<tabPane.getTabCount();i++) {
      if (tabPane.isEnabledAt(i)) {
        ensureVisibleTabAt(i);
        tabPane.setSelectedIndex(i);
        break;
      }
    }
  }

  protected void selectPreviousTab(int current) {
    for (int i=current-1;0<=i;i--) {
      if (tabPane.isEnabledAt(i)) {
        ensureVisibleTabAt(i);
        tabPane.setSelectedIndex(i);
        break;
      }
    }
  }

  /* not used
  protected int getPreviousTabIndex(int base) {
    int tabIndex = base - 1;
    return (tabIndex < 0? 0: tabIndex);
  }

  protected int getNextTabIndex(int base) {
    int tabIndex = base + 1;
    return (tabPane.getTabCount() <= tabIndex? tabIndex-1: tabIndex);
  }
  */


  //
  // these methods exist for innerclass
  //
  void setMaxTabHeight(int maxTabHeight) {
    this.maxTabHeight = maxTabHeight;
  }
    
  int getMaxTabHeight() {
    return maxTabHeight;
  }
  
  Rectangle[] getRects() {
    return rects;
  }
    
  SingleRowTabbedPane getTabbedPane() {
    return (SingleRowTabbedPane)tabPane;
  }
    
  protected FontMetrics getFontMetrics() {
    Font font = tabPane.getFont();
    return Toolkit.getDefaultToolkit().getFontMetrics(font);
  }

  protected int calculateMaxTabHeight(int tabPlacement) {
    return super.calculateMaxTabHeight(tabPlacement);
  }
    
  protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) {
    return super.calculateTabWidth(tabPlacement, tabIndex, metrics);
  }

  protected void assureRectsCreated(int tabCount) {
    super.assureRectsCreated(tabCount);
  }


  //
  // Layout
  //
  class SingleRowTabbedLayout extends BasicTabbedPaneUI.TabbedPaneLayout {
    JTabbedPane tabPane;
    
    SingleRowTabbedLayout(JTabbedPane tabPane) {
      this.tabPane = tabPane;
    }
    
    public void layoutContainer(Container parent) {
      super.layoutContainer(parent);
      if (tabPane.getComponentCount() < 1) {
        return;
      }

      int tabPlacement = tabPane.getTabPlacement();
      int maxTabHeight = calculateMaxTabHeight(tabPlacement);
      Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
      Insets        insets = tabPane.getInsets();
      Rectangle     bounds = tabPane.getBounds();
      
      SingleRowTabbedPane stabPane = (SingleRowTabbedPane)tabPane;
      Dimension         d = stabPane.getButtonPreferredSize();
      JButton[]   buttons = stabPane.getButtons();
      int buttonPlacement = stabPane.getButtonPlacement();
      
      int x,y;
      if (tabPlacement == TOP) {                            // TOP
        y = bounds.y + insets.top + tabAreaInsets.top;
      } else {                                              // BOTTOM
        y = bounds.y + bounds.height - insets.bottom
              - tabAreaInsets.bottom - maxTabHeight;
      }
      if (buttonPlacement == RIGHT) {                       // RIGHT
        x = bounds.x + bounds.width - insets.right;     
        for (int i=buttons.length-1;0<=i;i--) {
          x -= d.width;
          buttons[i].setBounds(x, y, d.width, d.height);
        }
      } else {                                              // LEFT
        x = bounds.x + insets.left;    
        for (int i=0;i<buttons.length;i++) {
          buttons[i].setBounds(x, y, d.width, d.height);
          x += d.width;
        }
      }
    }
    
    public void calculateLayoutInfo() {
      int tabCount = tabPane.getTabCount(); 
      assureRectsCreated(tabCount);
      calculateTabWidths(tabPane.getTabPlacement(), tabCount);    
      calculateTabRects(tabPane.getTabPlacement(), tabCount);    
    }
    

    protected void calculateTabWidths(int tabPlacement, int tabCount) {
      if (tabCount == 0) {
        return;
      }
      FontMetrics metrics = getFontMetrics();
      int fontHeight = metrics.getHeight();
      int maxTabHeight = calculateMaxTabHeight(tabPlacement);
      setMaxTabHeight(maxTabHeight);
      Rectangle[] rects = getRects();   
      for (int i = 0; i < tabCount; i++) {
        rects[i].width  = calculateTabWidth(tabPlacement, i, metrics);
        rects[i].height = maxTabHeight;
      }      
    }
  
    protected void calculateTabRects(int tabPlacement, int tabCount) {
      if (tabCount == 0) {
        return;
      }
      SingleRowTabbedPane stabPane = (SingleRowTabbedPane)tabPane;
      Dimension size = tabPane.getSize();
      Insets  insets = tabPane.getInsets(); 
      Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
      int selectedIndex = tabPane.getSelectedIndex();

      int maxTabHeight = getMaxTabHeight();
      int x = insets.left + tabAreaInsets.left;
      int y;
      if (tabPlacement == TOP) {
        y = insets.top + tabAreaInsets.top;
      } else {         // BOTTOM
        y = size.height - insets.bottom - tabAreaInsets.bottom - maxTabHeight;
      }
      
      int returnAt = size.width - (insets.right + tabAreaInsets.right);
      Rectangle[] rects = getRects();    
      int visibleStartIndex = stabPane.getVisibleStartIndex();    
      int visibleCount = 0;
    
      for (int i = visibleStartIndex; i < tabCount; i++) {
        Rectangle rect = rects[i];
        if (visibleStartIndex < i) {
          rect.x = rects[i-1].x + rects[i-1].width;
        } else {
          rect.x = x;
        }     

        if (rect.x + rect.width > returnAt) {
          break;
        } else {
          visibleCount++;
          rect.y = y;
        }
      }      
      stabPane.setVisibleCount(visibleCount);
      stabPane.setVisibleStartIndex(visibleStartIndex);
    }
  }
  
  //
  // Listener
  //
  protected class ShiftTabs implements ActionListener {
    SingleRowTabbedPane sPane;
    
    public void actionPerformed(ActionEvent e) {
      sPane = getTabbedPane();
      int index = getStartIndex();
      sPane.setVisibleStartIndex(index);
      sPane.repaint();
    }
    
    //public abstract int getStartIndex();
    protected int getStartIndex() {
      return 0;                          // first tab
    }
    
    protected int getStartIndex(int lastIndex) {
      Insets  insets = sPane.getInsets();
      Insets tabAreaInsets = getTabAreaInsets(sPane.getTabPlacement());
      int width = sPane.getSize().width
                         - (insets.left        + insets.right)
                         - (tabAreaInsets.left + tabAreaInsets.right);     
      int index;
      Rectangle[] rects = getRects();
      for (index=lastIndex;0<=index;index--) {
        width -= rects[index].width;
        if (width < 0) {
          break;
        }
      }
      return ++index;
    }
  }
  
}

